﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Utility.Configuration;
using Oracle.ManagedDataAccess.Client;
using System.Data.SqlClient;
using MySql.Data.MySqlClient;

namespace Utility.DBUtility
{
    /// <summary>
    /// 数据库类型
    /// </summary>
    public enum DatabaseType
    {
        /// <summary>
        /// 主库
        /// </summary>
        Master =0,
        /// <summary>
        /// 备库
        /// </summary>
        Backup =1
    }
    /// <summary>
    /// 链接字符串管理
    /// </summary>
    public class DBStringManage
    {
        private static DBStringManage instance;
        private static object syncRoot = new Object();
        private DBConnectionStringSection m_DBConnectionStringSection;
        /// <summary>
        /// 异常链接不访问时间(S)
        /// </summary>
        private int m_TimeOutSeconds = 60;
        /// <summary>
        /// 链接如果异常,进行记录,短时间内不使用异常的数据
        /// </summary>
        private ConcurrentDictionary<string, DateTime> m_brokenConnection = new ConcurrentDictionary<string, DateTime>();
        private DBStringManage()
        {
            m_DBConnectionStringSection = ConfigurationManager.GetSection("DBConnectionString") as DBConnectionStringSection;
        }
        /// <summary>
        /// 单例模式运行
        /// </summary>
        public static DBStringManage Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                        {
                            instance = new DBStringManage();
                        }
                    }
                }
                return instance;
            }
        }
        /// <summary>
        /// 获取数据库链接
        /// </summary>
        /// <param name="key">数据库链接名称</param>
        /// <param name="type">主备库类型(DatabaseType枚举)</param>
        /// <returns></returns>
        public string GetDbString(string key = "", DatabaseType type = DatabaseType.Master)
        {
            Begin:
            if (type == DatabaseType.Master)
            {
                if (string.IsNullOrEmpty(key)) //如果数据库链接名称为空,则取设置的Default =true 的节点
                {
                    string connectionString = m_DBConnectionStringSection.Master[0].Value;
                    int count = m_DBConnectionStringSection.Master.Count;
                    for (int i = 0; i < count; i++)
                    {
                        if (m_DBConnectionStringSection.Master[i].Default)
                        {
                            connectionString = m_DBConnectionStringSection.Master[i].Value;
                            break;
                        }
                    }
                    return connectionString;
                }
                else
                {
                    var master = m_DBConnectionStringSection.Master[key];
                    if (null != master)
                    {
                        return master.Value;
                    }
                    else
                    {
                        key = string.Empty;
                        goto Begin;
                    }
                }
            }
            else
            {
                BackupSection backupSection = null;
                int count = m_DBConnectionStringSection.Backup.Count;
                if (count == 0)
                {   //如果没有Backup节点,直接去找Master节点
                    type = DatabaseType.Master;
                    goto Begin;
                }
                if (string.IsNullOrEmpty(key))//如果数据库链接名称为空,则取设置的Default =true 的节点
                {
                    backupSection = m_DBConnectionStringSection.Backup[0];
                    for (int i = 0; i < count; i++)
                    {
                        if (m_DBConnectionStringSection.Backup[i].Default)
                        {
                            backupSection = m_DBConnectionStringSection.Backup[i];
                            break;
                        }
                    }
                }
                else
                {
                    backupSection = m_DBConnectionStringSection.Backup[key];
                }
                if (null == backupSection)
                {
                    type = DatabaseType.Master;
                    goto Begin;
                }
                else
                {
                    //判断获取到的链接是否有效,如果无效,则跳转到主库
                    if (!CheckSection(backupSection.Key))
                    {
                        type = DatabaseType.Master;
                        goto Begin;
                    }
                    //使用 Stopwatch 进行测试过性能,10000次,在链接是成功的情况下,打开关闭链接,耗时3270ms
                    System.Data.IDbConnection connection = CreateConnection(backupSection);                    
                    try
                    {
                        connection.Open();
                        if (connection.State != System.Data.ConnectionState.Open)
                        {
                            backupSection = null;
                            type = DatabaseType.Master;
                            goto Begin;
                        }
                        else
                        {
                            return backupSection.Value;
                        }
                    }
                    catch (Exception ex)
                    {   //如果出现异常,将链接的名称加入到异常链接中
                        if (m_brokenConnection.ContainsKey(backupSection.Key))
                        {  
                            m_brokenConnection[key] = DateTime.Now;
                        }
                        else
                        {
                            m_brokenConnection.TryAdd(key, DateTime.Now);
                        }
                        backupSection = null;
                        type = DatabaseType.Master;
                        goto Begin;
                    }
                    finally
                    {
                        if (null != connection)
                        {
                            connection.Close();
                            connection.Dispose();
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 获取数据库类型
        /// </summary>
        /// <param name="key">数据库链接名称</param>
        /// <param name="type">主备库类型(DatabaseType枚举)</param>
        /// <returns></returns>
        public string GetDbType(string key = "", DatabaseType type = DatabaseType.Master)
        {
            Begin:
            if (type == DatabaseType.Master)
            {
                if (string.IsNullOrEmpty(key))
                {
                    string dbType = m_DBConnectionStringSection.Master[0].Type;
                    int count = m_DBConnectionStringSection.Master.Count;
                    for (int i = 0; i < count; i++)
                    {
                        if (m_DBConnectionStringSection.Master[i].Default)
                        {
                            dbType = m_DBConnectionStringSection.Master[i].Type;
                            break;
                        }
                    }
                    return dbType;
                }
                else
                {
                    var master = m_DBConnectionStringSection.Master[key];
                    if (null != master)
                    {
                        return master.Type;
                    }
                    else
                    {
                        key = string.Empty;
                        goto Begin;
                    }
                }
            }
            else
            {
                int count = m_DBConnectionStringSection.Backup.Count;
                if (count == 0)
                {   //如果没有Backup节点,直接去找Master节点
                    type = DatabaseType.Master;
                    goto Begin;
                }
                if (string.IsNullOrEmpty(key))
                {
                    string dbType = m_DBConnectionStringSection.Backup[0].Type;
                    for (int i = 0; i < count; i++)
                    {
                        if (m_DBConnectionStringSection.Backup[i].Default)
                        {
                            dbType = m_DBConnectionStringSection.Backup[i].Type;
                            break;
                        }
                    }
                    return dbType;
                }
                else
                {
                    var backup = m_DBConnectionStringSection.Backup[key];
                    if (null != backup)
                    {
                        return backup.Type;
                    }
                    else
                    {
                        type = DatabaseType.Master;
                        goto Begin;
                    }
                }
            }
        }
        /// <summary>
        /// 判断链接是否有效
        /// </summary>
        /// <returns></returns>
        private bool CheckSection(string key)
        {
            DateTime dt;
            if (m_brokenConnection.TryGetValue(key, out dt))
            {
                if ((DateTime.Now - dt).TotalSeconds < m_TimeOutSeconds)
                {
                    return false;
                }
                else
                {
                    m_brokenConnection.TryRemove(key, out dt);
                }
            }
            return true;
        }
        /// <summary>
        /// 创建链接对象
        /// </summary>
        /// <param name="section"></param>
        /// <returns></returns>
        private System.Data.IDbConnection CreateConnection(MasterSection section)
        {
            System.Data.IDbConnection connection = null;
            switch (section.Type.ToUpper())
            {
                case "ORACLE":
                    connection = new OracleConnection(section.Value);
                    break;
                case "SQLSERVER":
                    connection = new SqlConnection(section.Value);
                    break;
                case "MYSQL":
                    connection = new MySqlConnection(section.Value);
                    break;
            }
            return connection;
        }
    }
}
